package com.freewater.javabase.demo.jackson.deserialization;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.ConstructorDetector;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.Validate;

@Slf4j
public class DeserializationDemo {
    public static void main(String[] args) throws JsonProcessingException {
        useJsonCreatorAnnotation();
        useModuleParameterNames();
    }

    public static void useJsonCreatorAnnotation() throws JsonProcessingException {
        String json = "{\"name\":\"freewater\",\"age\":18}";
        ObjectMapper objectMapper = new ObjectMapper();
        Person person = objectMapper.readValue(json, Person.class);
        log.info(person.toString());
    }

    // 如果是单参构造函数，jackson由于历史原因无法根据json属性映射到构造函数参数，下面的链接解决了此问题：
    // https://github.com/FasterXML/jackson-databind/issues/1498#issuecomment-695088258
    public static void useModuleParameterNames() throws JsonProcessingException {
        String json = "{\"name\":\"freewater\",\"age\":18,\"address\":\"chengdu\"}";
        ObjectMapper objectMapper = JsonMapper.builder()
                .constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
                .addModule(new ParameterNamesModule()) // 给JsonMapper显示注册上此模块
                .build();
        User user = objectMapper.readValue(json, User.class);
        log.info(user.toString());
    }

    @Getter
    @ToString
    static class Person {

        private final String name;

        private final Integer age;

        @JsonCreator
        public Person(@JsonProperty("name") String name,
                      @JsonProperty("age") Integer age) {
            Validate.notBlank(name, "name can not be empty");
            Validate.notNull(age, "age can not be null");
            Validate.isTrue(age >= 0, "age must be positive");
            this.name = name;
            this.age = age;
        }
    }

    @Getter
    @ToString
    static class User {

        private final String name;

        private final Integer age;

        private final String address;

        public User(String name) {
            this(name, 0, "");
        }

        public User(String name, Integer age) {
            this(name, age, "");
        }

        public User(String name, Integer age, String address) {
            Validate.notBlank(name, "name can not be empty");
            Validate.notNull(age, "age can not be null");
            Validate.isTrue(age >= 0, "age must be positive");
            this.name = name;
            this.age = age;
            this.address = address;
        }
    }
}
